#ifndef _SUBGRAPH_H
#define _SUBGRAPH_H

#include "Color.h"
#include "Point3.h"
#include "Matrix4.h"
#include "../Shapes/Shape.h"
#include <vector>

class Node;
class Trans;
class Subgraph;
class Object;

struct MatrixObject {
	MatrixObject(Matrix4 _m, Matrix4 _im, Object* _o) : m(_m), im(_im), o(_o), inTree(true){listName = 0;}
	~MatrixObject();
	Matrix4 m, im;
	Object* o;
	bool inTree;
	GLuint listName;
};

class Node {
public:
	Node(){};
	~Node(){};

	virtual void buildObjectList(Matrix4& m, Matrix4& im, std::list<MatrixObject>* oList)=0;

	enum NodeType {
		NODE,
		TRANS,
		SUBGRAPH,
		OBJECT
	};

	virtual NodeType getNodeType(){return NODE;}
};



class Trans : public Node{
public:
	Trans();
	~Trans();

	//Adds a new transformation to be applied
	void addTransformation(const Matrix4& m);
	void addScaling(double x=1, double y=1, double z=1);
	void addTranslation(double x=0, double y=0, double z=0);
	void addRotation(Vector3 axis, double angle);


	void setSub(Node*);
	
	void buildObjectList(Matrix4& m, Matrix4& im, std::list<MatrixObject>* oList);
	
	Node::NodeType getNodeType(){return Node::TRANS;}

private:
	//Accumulated matrices
	Matrix4 matrix, imatrix, 
		rotation, irotation, 
		translation, itranslation, 
		scaling, iscaling;
	Node* sub;
};

class Subgraph : public Node{
public:
	~Subgraph();
	//Adds a new Trans at the last position of transes
	void addTrans(Trans* t);
	void clearTranses();
	
	void buildObjectList(Matrix4& m, Matrix4& im, std::list<MatrixObject>* oList);

	Node::NodeType getNodeType(){return Node::SUBGRAPH;}

private:
	std::list<Trans*> transes;
};


class Object : public Node {
public:
	Object(Object* o);
	Object(Shape* s);
	Object();

	void draw();

	void buildObjectList(Matrix4& m, Matrix4& im, std::list<MatrixObject>* oList);
	
	void setAmbient(const Color& a){ambient = a;}
	void setDiffuse(const Color& d){diffuse = d;}
	void setSpecular(const Color& s){specular = s;}
	void setReflect(const Color& r){reflect = r;}
	void setTransparent(const Color& t){transparent = t;}
	void setEmit(const Color& e){emit = e;}
	void setEmission(const Color& e){emission = e;}
	void setShape(Shape* s){shape = s;}
	void setShine(double s){shine = s;}
	void setAlpha(double a){alpha = a;}
	void setIor(double i){ior = i;}
	void setTextureU(double u){textureU = u;}
	void setTextureV(double v){textureV = v;}
	void setTextureName(std::string n){textureName = n;}

	Color getAmbient(){return ambient;}
	Color getDiffuse(){return diffuse;}
	Color getSpecular(){return specular;}
	Color getReflect(){return reflect;}
	Color getTransparent(){return transparent;}
	Color getEmit(){return emit;}
	Color getEmission(){return emission;}
	double getShine(){return shine;}
	Shape* getShape(){return shape;}
	double getAlpha(){return alpha;}
	double getIor(){return ior;}
	double getTextureU(){return textureU;}
	double getTextureV(){return textureV;}
	std::string getTextureName(){return textureName;}
	Node::NodeType getNodeType(){return Node::OBJECT;}

	HitRecord intersect(Point3& point, Vector3& dir){return shape->intersect(point, dir);}
	
private:
	Shape* shape;
	Color ambient, diffuse, specular, reflect, transparent, emit, emission;
	double shine, alpha, ior, textureU, textureV;
	std::string textureName;
};

#endif